Conversation
sounil
requested review from
ar7casper,
dgeyshis,
shahar-davidson and
yotamleo
as code owners
April 15, 2026 00:12
Closed
21 tasks
This was referenced Aug 13, 2026
Collaborator
|
Thank you for building this, @sounil 🙏 — the web UI is a great addition. I've rebuilt it on current The new PR credits you as the original author; happy to adjust the attribution or fold in any feedback you have there. |
gadievron
added a commit
that referenced
this pull request
Aug 14, 2026
Adds an opt-in, loopback-only web UI (`openant serve`) that drives the existing scan pipeline from a browser: submit a repo URL or local path, watch scan logs stream over SSE, and read the HTML report / markdown summary / disclosures. Original work by Sounil Yu (PR #25, branch knostic/webui-2); this reconciles it onto current master (taking master on the 5 superseded Python-side conflicts, keeping only the additive Go/UI surface + a one-line serveCmd registration) and hardens it. Supersedes #25. Security posture — the server binds a loopback address (127.0.0.1 by default), EVERY route rejects a non-loopback Host (DNS-rebinding guard in the securityHeaders middleware, GET routes included), and every state-changing request additionally requires a per-instance crypto/rand CSRF token (constant-time compare) plus a same-origin check. So the read and write surfaces are both reachable only by a caller already on the local origin — this is defense-in-depth hardening, not a remotely-exploitable vulnerability. One residual caveat, tracked below: the live HTML report loads Tailwind/Chart.js from CDNs, so a CDN compromise could run script in the server origin. - git-clone SSRF guard (repoHostBlocked): classifies canonical IPs by value (loopback/link-local/unspecified/AWS IPv4+IPv6 IMDS/Alibaba blocked; RFC1918 + IPv6-ULA allowed for internal servers), resolves DNS names and blocks any sensitive result, and blocks ANY non-canonical numeric literal outright (encoding-proof for literals: decimal/octal/hex/short-form/mod-2^32-wrap, IPv6 zone-ids, IPv4-mapped forms). Fails closed on non-ASCII/IDNA hosts, backslash/ unparseable URLs, and scp multi-userinfo; blocks the .localhost TLD; passes git -c http.followRedirects=false. DNS-rebinding of a repo hostname between the check and the clone is out of scope (the guard covers literals + one resolution). - Rejects credentials embedded in an http(s) repo URL (they would be logged verbatim); the python subprocess runs with -P so a hostile openant/ package in the scanned repo can't shadow the real module. - DOMPurify 3.4.13 (vendored, no CDN; updated from 3.1.6 = CVE-2025-15599 / CVE-2026-41238) with a strict INERT allowlist (no style/form/img/svg — a repo can break out of a markdown code fence, so the default allowlist's active content is refused) on client-rendered markdown + a strict bluemonday allowlist on the report SafeRemediation path; the API-key value is never sent to any page. - Robustness: managed-writer stderr with WaitDelay (no deadlock on a detached child; a WaitDelay-tripped exit-0 scan keeps its output rather than erroring), signal-exit treated as error, LogBuf bounded by line count AND bytes, delete that waits (up to 5s) for the runner before removing the dir, and a synchronized shutdown (drainMu + WaitShutdown) that cancels in-flight scans before exit. - The job output dir holds files derived from the untrusted repo, so served files (report.html, summary, disclosures) are opened O_NOFOLLOW and confirmed to be regular files within the job dir (a symlink to a host secret is refused atomically at open — no check-then-read race), and the clone is time-bounded (15m) so a hostile remote can't hold a scan slot forever. Tested: host-classification suite (41 blocked forms + a legit allow-set), auth-gate rejections (rebinding Host, cross-site, foreign Origin, credential URL), remediation-XSS, job-lifecycle (drain/cancel/recover/delete-waits-for-runner), the SSE Last-Event-ID overflow clamp, and the stderr WaitDelay mechanism. go build / go vet / go test ./... green; go test -race clean; semgrep + CodeQL (CWE-918 SSRF = 0); real end-to-end scans of Python/Go/JS repos through the UI. Deferred (tracked, not in this PR): vendoring the report-template CDN scripts (Tailwind is a CDN JIT; needs an asset build step, and the template is shared with `openant report -f html`) and the CSP/COEP that depends on it; the marked 12.0.2 update (no known CVE; output is DOMPurify-sanitized); the unbounded stdout capture buffer (openant's stdout is its own bounded JSON envelope). Process-group kill (Setpgid/SIGKILL) and O_NOFOLLOW are Unix-only, so they live in procgroup_unix.go / procgroup_other.go build-tagged files; the code builds and vets clean on linux, darwin, and windows. Reproduce: git fetch origin && git checkout <this-branch> cd apps/openant-cli && go build -o /tmp/openant-cli . && /tmp/openant-cli serve --addr 127.0.0.1:8765 go test ./... Co-authored-by: Sounil Yu <4305467+sounil@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gadievron
added a commit
that referenced
this pull request
Aug 14, 2026
Adds an opt-in, loopback-only web UI (`openant serve`) that drives the existing scan pipeline from a browser: submit a repo URL or local path, watch scan logs stream over SSE, and read the HTML report / markdown summary / disclosures. Original work by Sounil Yu (PR #25, branch knostic/webui-2); this reconciles it onto current master (taking master on the 5 superseded Python-side conflicts, keeping only the additive Go/UI surface + a one-line serveCmd registration) and hardens it. Supersedes #25. Security posture — the server binds a loopback address (127.0.0.1 by default), EVERY route rejects a non-loopback Host (DNS-rebinding guard in the securityHeaders middleware, GET routes included), and every state-changing request additionally requires a per-instance crypto/rand CSRF token (constant-time compare) plus a same-origin check. So the read and write surfaces are both reachable only by a caller already on the local origin — this is defense-in-depth hardening, not a remotely-exploitable vulnerability. One residual caveat, tracked below: the live HTML report loads Tailwind/Chart.js from CDNs, so a CDN compromise could run script in the server origin. - git-clone SSRF guard (repoHostBlocked): classifies canonical IPs by value (loopback/link-local/unspecified/AWS IPv4+IPv6 IMDS/Alibaba blocked; RFC1918 + IPv6-ULA allowed for internal servers), resolves DNS names and blocks any sensitive result, and blocks ANY non-canonical numeric literal outright (encoding-proof for literals: decimal/octal/hex/short-form/mod-2^32-wrap, IPv6 zone-ids, IPv4-mapped forms). Fails closed on non-ASCII/IDNA hosts, backslash/ unparseable URLs, and scp multi-userinfo; blocks the .localhost TLD; passes git -c http.followRedirects=false. DNS-rebinding of a repo hostname between the check and the clone is out of scope (the guard covers literals + one resolution). - Rejects credentials embedded in an http(s) repo URL (they would be logged verbatim); the python subprocess runs with -P so a hostile openant/ package in the scanned repo can't shadow the real module. - DOMPurify 3.4.13 (vendored, no CDN; updated from 3.1.6 = CVE-2025-15599 / CVE-2026-41238) with a strict INERT allowlist (no style/form/img/svg — a repo can break out of a markdown code fence, so the default allowlist's active content is refused) on client-rendered markdown + a strict bluemonday allowlist on the report SafeRemediation path; the API-key value is never sent to any page. - Robustness: managed-writer stderr with WaitDelay (no deadlock on a detached child; a WaitDelay-tripped exit-0 scan keeps its output rather than erroring), signal-exit treated as error, LogBuf bounded by line count AND bytes, delete that waits (up to 5s) for the runner before removing the dir, and a synchronized shutdown (drainMu + WaitShutdown) that cancels in-flight scans before exit. - The job output dir holds files derived from the untrusted repo, so served files (report.html, summary, disclosures) are opened O_NOFOLLOW and confirmed to be regular files within the job dir (a symlink to a host secret is refused atomically at open — no check-then-read race), and the clone is time-bounded (15m) so a hostile remote can't hold a scan slot forever. Tested: host-classification suite (41 blocked forms + a legit allow-set), auth-gate rejections (rebinding Host, cross-site, foreign Origin, credential URL), remediation-XSS, job-lifecycle (drain/cancel/recover/delete-waits-for-runner), the SSE Last-Event-ID overflow clamp, and the stderr WaitDelay mechanism. go build / go vet / go test ./... green; go test -race clean; semgrep + CodeQL (CWE-918 SSRF = 0); real end-to-end scans of Python/Go/JS repos through the UI. Deferred (tracked, not in this PR): vendoring the report-template CDN scripts (Tailwind is a CDN JIT; needs an asset build step, and the template is shared with `openant report -f html`) and the CSP/COEP that depends on it; the marked 12.0.2 update (no known CVE; output is DOMPurify-sanitized); the unbounded stdout capture buffer (openant's stdout is its own bounded JSON envelope). Process-group kill (Setpgid/SIGKILL) and O_NOFOLLOW are Unix-only, so they live in procgroup_unix.go / procgroup_other.go build-tagged files; the code builds and vets clean on linux, darwin, and windows. Reproduce: git fetch origin && git checkout <this-branch> cd apps/openant-cli && go build -o /tmp/openant-cli . && /tmp/openant-cli serve --addr 127.0.0.1:8765 go test ./... Documents the command in the top-level README (### Web UI under "Analyzing a project"). Co-authored-by: Sounil Yu <4305467+sounil@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gadievron
added a commit
that referenced
this pull request
Aug 14, 2026
Adds an opt-in, loopback-only web UI (`openant serve`) that drives the existing scan pipeline from a browser: submit a repo URL or local path, watch scan logs stream over SSE, and read the HTML report / markdown summary / disclosures. Original work by Sounil Yu (PR #25, branch knostic/webui-2); this reconciles it onto current master (taking master on the 5 superseded Python-side conflicts, keeping only the additive Go/UI surface + a one-line serveCmd registration) and hardens it. Supersedes #25. Security posture — the server binds a loopback address (127.0.0.1 by default), EVERY route rejects a non-loopback Host (DNS-rebinding guard in the securityHeaders middleware, GET routes included), and every state-changing request additionally requires a per-instance crypto/rand CSRF token (constant-time compare) plus a same-origin check. So the read and write surfaces are both reachable only by a caller already on the local origin — this is defense-in-depth hardening, not a remotely-exploitable vulnerability. One residual caveat, tracked below: the live HTML report loads Tailwind/Chart.js from CDNs, so a CDN compromise could run script in the server origin. - git-clone SSRF guard (repoHostBlocked): classifies canonical IPs by value (loopback/link-local/unspecified/AWS IPv4+IPv6 IMDS/Alibaba blocked; RFC1918 + IPv6-ULA allowed for internal servers), resolves DNS names and blocks any sensitive result, and blocks ANY non-canonical numeric literal outright (encoding-proof for literals: decimal/octal/hex/short-form/mod-2^32-wrap, IPv6 zone-ids, IPv4-mapped forms). Fails closed on non-ASCII/IDNA hosts, backslash/ unparseable URLs, and scp multi-userinfo; blocks the .localhost TLD; passes git -c http.followRedirects=false. DNS-rebinding of a repo hostname between the check and the clone is out of scope (the guard covers literals + one resolution). - Rejects credentials embedded in an http(s) repo URL (they would be logged verbatim); the python subprocess runs with -P so a hostile openant/ package in the scanned repo can't shadow the real module. - DOMPurify 3.4.13 (vendored, no CDN; updated from 3.1.6 = CVE-2025-15599 / CVE-2026-41238) with a strict INERT allowlist (no style/form/img/svg — a repo can break out of a markdown code fence, so the default allowlist's active content is refused) on client-rendered markdown + a strict bluemonday allowlist on the report SafeRemediation path; the API-key value is never sent to any page. - Robustness: managed-writer stderr with WaitDelay (no deadlock on a detached child; a WaitDelay-tripped exit-0 scan keeps its output rather than erroring), signal-exit treated as error, LogBuf bounded by line count AND bytes, delete that waits (up to 5s) for the runner before removing the dir, and a synchronized shutdown (drainMu + WaitShutdown) that cancels in-flight scans before exit. - The job output dir holds files derived from the untrusted repo, so served files (report.html, summary, disclosures) are opened O_NOFOLLOW and confirmed to be regular files within the job dir (a symlink to a host secret is refused atomically at open — no check-then-read race), and the clone is time-bounded (15m) so a hostile remote can't hold a scan slot forever. Tested: host-classification suite (41 blocked forms + a legit allow-set), auth-gate rejections (rebinding Host, cross-site, foreign Origin, credential URL), remediation-XSS, job-lifecycle (drain/cancel/recover/delete-waits-for-runner), the SSE Last-Event-ID overflow clamp, and the stderr WaitDelay mechanism. go build / go vet / go test ./... green; go test -race clean; semgrep + CodeQL (CWE-918 SSRF = 0); real end-to-end scans of Python/Go/JS repos through the UI. Deferred (tracked, not in this PR): vendoring the report-template CDN scripts (Tailwind is a CDN JIT; needs an asset build step, and the template is shared with `openant report -f html`) and the CSP/COEP that depends on it; the marked 12.0.2 update (no known CVE; output is DOMPurify-sanitized); the unbounded stdout capture buffer (openant's stdout is its own bounded JSON envelope). Process-group kill (Setpgid/SIGKILL) and O_NOFOLLOW are Unix-only, so they live in procgroup_unix.go / procgroup_other.go build-tagged files; the code builds and vets clean on linux, darwin, and windows. Reproduce: git fetch origin && git checkout <this-branch> cd apps/openant-cli && go build -o /tmp/openant-cli . && /tmp/openant-cli serve --addr 127.0.0.1:8765 go test ./... Documents the command in the top-level README (### Web UI under "Analyzing a project"). Co-authored-by: Sounil Yu <4305467+sounil@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User friendly web interface for those who don't want to use the CLI. Also makes for nice demos.
Full description of specifications are at WEBUI_SPEC.md